/**
 * Copyright (C) 2025 pony working team
 * SPDX-License-Identifier: MulanPSL-2.0
 */

/* includes (local) ----------------------------------------------------------*/
#include <hal.h>
/* includes (standard library, system) ---------------------------------------*/
#include <string.h>
/* includes (other library) --------------------------------------------------*/
/* includes (project) --------------------------------------------------------*/
#include <x_config.h>
#include <x_module.h>
#include <x_errno.h>
#include <vlog.h>

/* defines -------------------------------------------------------------------*/
/* typedefs ------------------------------------------------------------------*/
/* macro ---------------------------------------------------------------------*/
/* variables (extern) --------------------------------------------------------*/
/* variables (local) ---------------------------------------------------------*/
static dlist_t s_devs = DLIST_INIT(s_devs);
static k_mutex_t s_dev_lock;
/* variables (global) --------------------------------------------------------*/
/* inlines -------------------------------------------------------------------*/
/* functions -----------------------------------------------------------------*/

hal_dev_t *hal_find(const char *name)
{
    dlist_t *iter;
    hal_dev_t *dev = NULL;
    dlist_foreach(iter, &s_devs)
    {
        dev = container_of(iter, hal_dev_t, node);
        if (strcmp(name, dev->name) == 0) {
            return dev;
        }
    }
    return NULL;
}

hal_dev_t *hal_open(const char *name, int oflag, ...)
{
    if (name == NULL) {
        errno = EINVAL;
        return NULL;
    }

    k_mutex_lock(&s_dev_lock);
    hal_dev_t *dev = hal_find(name);
    k_mutex_unlock(&s_dev_lock);

    if (dev == NULL) {
        errno = ENOENT;
        VLOG_E("device '%s' not found", name);
        return NULL;
    }

    // Check for open count overflow
    if (dev->open_cnt + 1 < dev->open_cnt) {
        errno = EMLINK;
        return NULL;
    }

    // If already opened, just return the device
    if (dev->open_cnt++ > 0) {
        return dev;
    }

    // Call device-specific open operation if available
    if (dev->ops != NULL && dev->ops->open != NULL) {
        va_list valist;
        va_start(valist, oflag);
        if (dev->ops->open(dev, oflag, valist) != 0) {
            errno = EIO;
            dev = NULL;
        }
        va_end(valist);
    }

    // Increment open count if open succeeded (open firstly)
    if (dev != NULL) {
        ++dev->open_cnt;
    }

    return dev;
}

int hal_close(hal_dev_t *dev)
{
    if (dev == NULL) {
        return -EINVAL;
    }
    if (dev->ops == NULL || dev->ops->close == NULL) {
        return -ENOSYS;
    }
    if (dev->open_cnt == 0) {
        return -ENXIO;
    }
    if (dev->ops->close(dev) != 0) {
        return -EIO;
    }
    --dev->open_cnt;
    return 0;
}

int hal_register(hal_dev_t *dev)
{
    if (dev == NULL || dev->name == NULL) {
        return -EINVAL;
    }
    if (k_mutex_lock(&s_dev_lock) != 0) {
        return -EPERM;
    }
    if (hal_find(dev->name) != NULL) {
        k_mutex_unlock(&s_dev_lock);
        return -EEXIST;
    }
    dlist_add_tail(&s_devs, &dev->node);
    k_mutex_unlock(&s_dev_lock);

    return 0;
}

int hal_unregister(hal_dev_t *dev)
{
    if (dev == NULL) {
        return -EINVAL;
    }
    dlist_del(&dev->node);

    return 0;
}

int hal_dev_init(hal_dev_t *dev, const hal_dev_ops_t *ops)
{
    if (dev == NULL || ops == NULL) {
        return -EINVAL;
    }
    dev->ops = ops;

    return 0;
}

int hal_dev_open(hal_dev_t *dev, int oflags, ...)
{
    if (dev == NULL) {
        return -EINVAL;
    }
    if (dev->ops == NULL || dev->ops->open == NULL) {
        return -ENOSYS;
    }
    va_list valist;
    va_start(valist, oflags);
    int ret = dev->ops->open(dev, oflags, valist);
    va_end(valist);

    return ret;
}

static int hal_init(void)
{
    return k_mutex_init(&s_dev_lock);
}

MODULE_INIT(hal, hal_init);

#ifdef CONFIG_CMDER

#include <cmder.h>

static int __cmd_dev(cmder_t *cmder, int argc, char *argv[])
{
    dlist_t *iter;
    hal_dev_t *dev = NULL;

    dlist_foreach(iter, &s_devs)
    {
        dev = container_of(iter, hal_dev_t, node);
        cmder_printf(cmder, "%s\n", dev->name);
    }

    return 0;
}

CMDER_CMD_DEF("dev", "show device driver", __cmd_dev);

#endif
